<?php

namespace app\queue\redis;

use constant\RedisConstant;
use util\RedisTool;
use util\WorkWxTool;
use Webman\RedisQueue\Consumer;

class TaskUserListQueue implements Consumer
{
    public $queue = RedisConstant::TaskUserList;

    //消费
    public function consume($data)
    {
        $taskUserName = $data['name'];
        $taskSign = $data['sign'];
        $assignMode = $data['mode'];
        $assignDuty = $data['duty'];
        $assignData = $data['data'];
        $userList = bubblingSort($assignData, 'weight', 'desc');
        if ($assignMode) {
            $redisKey = $taskUserName . RedisConstant::TaskAssignNormal . $taskSign;
            //获取本该下一个用户
            $firstUserId = getFirstUserId($assignMode, $redisKey, array_column($userList, 'user_id'));
            RedisTool::del($redisKey, 'list');
            switch ($assignMode) {
                case 1:
                    foreach ($userList as $k => $v) {
                        if ($v['user_id'] == $firstUserId) {
                            if (($k + 1) != count($userList)) {
                                $arr1 = array_slice($userList, 0, $k);
                                $arr2 = array_slice($userList, $k, count($userList) - $k);
                                $userList = array_merge($arr2, $arr1);
                                break;
                            }
                        }
                    }
                    /**
                     * 顺序派发
                     * 用户<==队列左出右进获取
                     * 固定派发固定模式为10
                     */
                    foreach ($userList as $user) {
                        //不参与派发人员跳过后续判断
                        if (!$user['in_queue']) {
                            RedisTool::set_rPush($redisKey, array_merge($user, ['assign_mode' => $assignMode, 'in_assign' => 0]), 'list');
                            continue;
                        }
                        //处理固定派发，将其置入队列左侧
                        if ($user['is_regular']) {
                            $exist = RedisTool::get_exists($taskUserName . RedisConstant::TaskReceiveRegular . $taskSign . '_' . $user['user_id'], 'list');
                            if (!$exist) RedisTool::set_lPush($redisKey, array_merge($user, ['assign_mode' => 10, 'in_assign' => 1]), 'list');
                        }
                        $in_assign = 1;
                        //处理普通用户，置入队列右侧
                        if ($user['receive_times'] > 0) {
                            //如果已达领取上限则跳过用户
                            $times = RedisTool::get_time($taskUserName . RedisConstant::TaskReceiveTimes . $taskSign . '_' . $user['user_id'], 'list');
                            if ($times >= $user['receive_times']) $in_assign = 0;
                        }
                        //休息的人也跳过用户
                        if ($user['is_rest']) $in_assign = 0;
                        RedisTool::set_rPush($redisKey, array_merge($user, ['assign_mode' => $assignMode, 'in_assign' => $in_assign]), 'list');
                    }
                    break;
                case 2:
                    foreach ($userList as $k => $v) {
                        if ($v['user_id'] == $firstUserId) {
                            if (($k + 1) != count($userList)) {
                                $arr1 = array_slice($userList, 0, $k);
                                $arr2 = array_slice($userList, $k, count($userList) - $k - 1);
                                $userList = array_merge($arr2, $arr1);
                                break;
                            }
                        }
                    }
                    /**
                     * 指定派发
                     * 用户<==队列中只含is_appoint的用户
                     * 且指定不受休息影响以及数量限制影响
                     */
                    foreach ($userList as $user) {
                        if ($user['is_appoint']) RedisTool::set_rPush($redisKey, array_merge($user, ['assign_mode' => $assignMode, 'in_assign' => 1]), 'list');
                    }
                    break;
                case 3:
                    /**
                     * 比例派发
                     * 比例算法 权重/（领取次数+1）
                     * 用户<==有序列表取分数最高的一个
                     */
                    foreach ($userList as $user) {
                        //不参与派发人员跳过后续判断
                        if (!$user['in_queue']) {
                            RedisTool::set_zAdd($redisKey, array_merge($user, ['assign_mode' => $assignMode, 'in_assign' => 0]), 0, 'list');
                            continue;
                        }
                        $times = RedisTool::get_time($taskUserName . RedisConstant::TaskReceiveTimes . $taskSign . '_' . $user['user_id'], 'list');
                        //比例算法 权重/（领取次数+1）
                        $ratio = $user['weight'] ? bcdiv($user['weight'], ($times ?: 0) + 1, 2) : 0;
                        //如果次数已满则不再派发
                        if ($user['receive_times'] > 0 && $times >= $user['receive_times']) $ratio = 0;
                        RedisTool::set_zAdd($redisKey, array_merge($user, ['assign_mode' => $assignMode, 'in_assign' => $ratio > 0 ? 1 : 0]), $ratio, 'list');
                    }
                    break;
                case 4:
                    /**
                     * 均等分配
                     * 用户<==有序列表取分数最低的一个
                     */
                    foreach ($userList as $user) {
                        //不参与派发人员跳过后续判断
                        if (!$user['in_queue']) {
                            RedisTool::set_zAdd($redisKey, array_merge($user, ['assign_mode' => $assignMode, 'in_assign' => 0]), 9999, 'list');
                            continue;
                        }
                        $score = RedisTool::get_time($taskUserName . RedisConstant::TaskReceiveTimes . $taskSign . '_' . $user['user_id'], 'clue');
                        if ($user['receive_times'] > 0 && $score >= $user['receive_times']) $score = 0;
                        RedisTool::set_zAdd($redisKey, array_merge($user, ['assign_mode' => $assignMode, 'in_assign' => 1]), $score, 'list');
                    }
                    break;
            }
        }
        if ($assignDuty) {
            $redisKey = $taskUserName . RedisConstant::TaskAssignDuty . $taskSign;
            //获取本该下一个用户
            $firstUserId = getFirstUserId($assignMode, $redisKey, array_column($userList, 'user_id'));
            foreach ($userList as $k => $v) {
                if ($v['user_id'] == $firstUserId) {
                    if (($k + 1) != count($userList)) {
                        $arr1 = array_slice($userList, 0, $k);
                        $arr2 = array_slice($userList, $k, count($userList) - $k - 1);
                        $userList = array_merge($arr2, $arr1);
                        break;
                    }
                }
            }
            RedisTool::del($redisKey, 'clue');
            /**
             * 值班分配
             * 用户<==值班队列中获取
             */
            foreach ($userList as $user) {
                //不在值班内
                if (!$user['is_duty'] && !$user['long_duty']) {
                    RedisTool::set_rPush($taskUserName . RedisConstant::TaskAssignDuty . $taskSign, ['uid' => $user['user_id'], 'assign_mode' => $assignMode, 'in_assign' => 0], 'list');
                    continue;
                }
                $in_assign = 1;
                if ($user['duty_times'] > 0) {
                    $times = RedisTool::get_time($taskUserName . RedisConstant::TaskDutyTimes . $taskSign . '_' . $user['user_id'], 'clue');
                    if ($times >= $user['duty_times']) $in_assign = 0;
                }
                RedisTool::set_rPush($taskUserName . RedisConstant::TaskAssignDuty . $taskSign, array_merge($user, ['allocation_mode' => 8, 'in_assign' => $in_assign]), 'clue');
            }
        }
    }

    // 消费失败回调
    /*
    $package = [
        'id' => 1357277951, // 消息ID
        'time' => 1709170510, // 消息时间
        'delay' => 0, // 延迟时间
        'attempts' => 2, // 消费次数
        'queue' => 'send-mail', // 队列名
        'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
        'max_attempts' => 5, // 最大重试次数
        'error' => '错误信息' // 错误信息
    ]
    */
    public
    function onConsumeFailure(\Throwable $e, $package)
    {
        if (WorkWxTool::ErrorPush)
            WorkWxTool::ErrorMsg('用户派发顺序队列执行错误', $e->getMessage() . '\n' . json_encode($package['data'], 256));
    }
}